package hex.rng;

import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Simple XorShiftRNG.
 *
 * Note: According to RF benchmarks it does not provide so accurate results
 * as {@link java.util.Random}, however it can be used as an alternative.
 *
 */
public class XorShiftRNG extends Random {

  private AtomicLong _seed;

  public XorShiftRNG (long seed) {
    this._seed = new AtomicLong(seed);
  }

  @Override
  public long nextLong() {
    long oldseed, nextseed;
    AtomicLong seed = this._seed;
    do {
      oldseed = seed.get();
      nextseed = xorShift(oldseed);
    } while (!seed.compareAndSet(oldseed, nextseed));

    return nextseed;
  }

  @Override
  public int nextInt() {
    return nextInt(Integer.MAX_VALUE);
  }

  @Override
  public int nextInt(int n) {
    int r = (int) (nextLong() % n);
    return r > 0 ? r : -r;
  }

  @Override
  protected int next(int bits) {
    long nextseed = nextLong();
    return (int) (nextseed & ((1L << bits) - 1));
  }

  private long xorShift(long x) {
    x ^= (x << 21);
    x ^= (x >>> 35);
    x ^= (x << 4);
    return x;
  }
}
